#include <stdint.h>
#include <memory.h>
#include <text_video.h>
#include <task.h>
#include <mp_tables.h>

static struct mp_processor mp_processor[8];
static struct mp_bus mp_bus[8];
static struct mp_io_apic mp_io_apic[4];
static struct mp_io_interrupt mp_io_interrupt[4*24]; // # of IO APICs * MAX # of IO Redirections per IO APIC
static struct mp_local_interrupt mp_local_interrupt[32];

static const char * fps_sig = "_MP_";                // this a 'char' array, not an uint8_t array

static void parse_mp_config_tables(uint32_t offset)
{
    uint16_t entry_count = 0;
    uint32_t address_of_local_apic = 0;
    uint32_t processor_entries = 0, bus_entries = 0;
    uint32_t io_apic_entries = 0, io_interrupt_entries = 0;
    uint32_t local_interrupt_entries = 0;
        
    entry_count = *(uint16_t*)(offset + 0x22);
    address_of_local_apic = *(uint16_t*)(offset + 0x24);
        
    offset += 0x2C; /* jump over the header */

    while(entry_count--)
    {
        GUTS();
        
        switch(*(uint8_t*)offset)
        {
            case 0x00:
                if(processor_entries >= 8)
                {offset += 0x14; break;}
                
                copy_memory((uint8_t*)&mp_processor[processor_entries], (void *)(offset), 0x14);
                
                if((mp_processor[processor_entries].cpu_flags & 0x01) == 0)
                {printf("SYS: unusable CPU found! \n");}
                
                processor_entries++;
                offset += 0x14;
                break;
                    
            case 0x01:
                if(bus_entries >= 8)
                {offset += 0x08; break;}
                        
                clear_memory((uint8_t*)&mp_bus[bus_entries].bus_type_string, 7);
                copy_memory((uint8_t*)&mp_bus[bus_entries], (void *)(offset), 0x08);

                offset += 0x08;
                bus_entries++;
                break;  

            case 0x02:
                if(io_apic_entries >= 8)
                {offset += 0x08; break;}
                
                copy_memory((uint8_t*)&mp_io_apic[io_apic_entries], (void *)(offset), 0x08);

                offset += 0x08;
                io_apic_entries++;    
                break;

            case 0x03:
                copy_memory((uint8_t*)&mp_io_interrupt[io_interrupt_entries], (void *)(offset), 0x08);
                offset += 0x08;
                io_interrupt_entries++;
                break;

            case 0x04:
                copy_memory((uint8_t*)&mp_local_interrupt[local_interrupt_entries], (void *)(offset), 0x08);
                offset += 0x08;
                local_interrupt_entries++;
                break;
                    
            default:
                entry_count = 0;
                break;
        }
    }
    
    mp.processor_count = processor_entries;
    mp.bus_count = bus_entries;
    mp.io_apic_count = io_apic_entries;
    mp.io_interrupt_count = io_interrupt_entries;
    mp.local_interrupt_count = local_interrupt_entries;
}

static void parse_mp_fps(uint32_t offset)
{
    uint32_t mp_config_address = *(uint32_t*)(offset + 4);
    
    parse_mp_config_tables(mp_config_address);
}

static void cleanup_task(void)
{
    kill_task(active_task_id);
}

void parse_mp_tables(void)
{
    uint32_t i;                     /* we really don't want this to get optimized away */
    
    mp.processor_count = 0;         /* just incase something goes wrong, we can just assume there is 1 cpu */

    GUTS();

    for(i = 0x9FC00; i < 0xA0000; i += 16)  /* scan area 1 */
    {
        if(!compare_memory((uint8_t*)i, (uint8_t*)fps_sig, 4))
        {
            parse_mp_fps(i);
            cleanup_task();
        }
    }
    
    GUTS();
    
    for(i = 0xF0000; i < 0x100000; i += 16) /* scan area 2 */
    {
        if(!compare_memory((uint8_t*)i, (uint8_t*)fps_sig, 4))
        {
            parse_mp_fps(i);
            cleanup_task();
        }
    }
    printf("MP: FPS was not found\n");

    cleanup_task();
}

